﻿using BSF.BaseService.NScript.Compiler;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;

namespace BSF.BaseService.NScript.Core
{
    /// <summary>
    /// 编译信息上下文
    /// </summary>
    public class CompilerInfoContext
    {
        /// <summary>
        /// 编译信息解析结果缓存
        /// </summary>
        public static StringParser<CompilerInfo> CompilerInfoCache = new StringParser<CompilerInfo>();
    }
    /// <summary>
    /// 编译信息
    /// </summary>
    [Serializable]
    public class CompilerInfo
    {
       
        /// <summary>
        /// 其他编译文件路径
        /// </summary>
        public List<CompilerFileInfo> CodeFilePaths = new List<CompilerFileInfo>();
        /// <summary>
        /// Dll文件编译路径
        /// </summary>
        public List<CompilerFileInfo> DllFiles = new List<CompilerFileInfo>();
        /// <summary>
        /// 主文件路径信息
        /// </summary>
        public CompilerFileInfo MainFilePath = new CompilerFileInfo();
        /// <summary>
        /// 主文件代码内容
        /// 主文件：包含编译信息
        /// </summary>
        public string MainFileContent;
        /// <summary>
        /// 编译语言
        /// </summary>
        public EnumCompilerLanguage EnumCompilerLanguage = EnumCompilerLanguage.CSharp;
        /// <summary>
        /// 编译模式
        /// </summary>
        public EnumCompilerMode EnumCompilerMode = EnumCompilerMode.Assembly;
        /// <summary>
        /// 源码类型
        /// </summary>
        public EnumSourceType EnumSourceType = EnumSourceType.File;

        public CompilerInfo(){}
        public CompilerInfo Parse(EnumSourceType enumSourceType,string codeOrFileName)
        {
            if (enumSourceType == Core.EnumSourceType.Code)
            {
                string cacheKey = "主文件内容:" + codeOrFileName.GetHashCode();
                return CompilerInfoContext.CompilerInfoCache.Parser(cacheKey, () =>
                {
                    string content = codeOrFileName;
                    var compilerInfo = new CompilerInfo();
                    compilerInfo.Parse(codeOrFileName);
                    compilerInfo.MainFileContent = codeOrFileName;
                    compilerInfo.EnumSourceType = enumSourceType;
                    return compilerInfo;
                });
            }
            else if (enumSourceType == Core.EnumSourceType.File)
            {
                CompilerFileInfo compilerFileInfo = new CompilerFileInfo(codeOrFileName);
                string cacheKey = "主文件路径:" + codeOrFileName+"文件修改时间:" + compilerFileInfo.LastWriteTime.ToString("yyMMddHHmmssfff");
                return CompilerInfoContext.CompilerInfoCache.Parser(cacheKey, () =>
                {
                    string content = File.ReadAllText(compilerFileInfo.FullFileName);
                    var compilerInfo = new CompilerInfo();
                    compilerInfo.MainFilePath = compilerFileInfo;
                    compilerInfo.Parse(content);
                    compilerInfo.EnumSourceType = enumSourceType;
                    return compilerInfo;
                });
            }
            return null;
        }

        public int CompilerHashCode()
        {
            return MainFileContent.GetHashCode();
        }


        /*解析格式:key=value;key=value;
         * ,分割多个value
         */
        public void Parse(string content)
        { 
            //暂时使用自己写的算法,未来使用正则提取更优雅
            int startIndex = content.IndexOf("/*");
            if (startIndex > -1)
            {
                int lastIndex = content.IndexOf("*/");
                if (lastIndex > -1)
                {
                    Dictionary<string, string> dic = new Dictionary<string, string>();
                    string str = content.Substring(startIndex + 2, lastIndex - startIndex - 2);
                    string[] infoSplits = str.Split(';');
                    foreach (var s in infoSplits)
                    {
                        string[] kvSplits = s.Split('=');
                        if (kvSplits.Length == 2)
                        {
                            int keyStartIndex = kvSplits[0].LastIndexOf(' ');
                            if(keyStartIndex<=-1)
                                keyStartIndex =0;
                            string key = kvSplits[0].Substring(keyStartIndex, kvSplits[0].Length - keyStartIndex);
                            dic.Add(ReplaceSpecialChar(key).ToLower(),
                                ReplaceSpecialChar(kvSplits[1]));
                        }
                    }
                    Parse(dic);

                }
            }
        }

        public void Parse(Dictionary<string, string> dic)
        {
            if (dic.ContainsKey("codefiles"))
            {
                foreach (var fileName in GetValues(dic["codefiles"]))
                {
                    CodeFilePaths.Add(new CompilerFileInfo(fileName, this.MainFilePath.FullFileName));
                }
            }
            if (dic.ContainsKey("codefilepaths"))
            {
                foreach (var fileName in GetValues(dic["codefilepaths"]))
                {
                    CodeFilePaths.Add(new CompilerFileInfo(fileName, this.MainFilePath.FullFileName));
                }
            }
           
            if (dic.ContainsKey("dllfiles"))
            {
                foreach (var fileName in GetValues(dic["dllfiles"]))
                {
                    DllFiles.Add(new CompilerFileInfo(fileName, this.MainFilePath.FullFileName,false));
                }
            }
            if (dic.ContainsKey("compilerlanguage"))
            {
                EnumCompilerLanguage = (EnumCompilerLanguage)Enum.Parse(typeof(EnumCompilerLanguage), dic["compilerlanguage"],true);
            }
        }

        private List<string> GetValues(string value)
        { 
            var strs = value.Trim(',').Split(',');
            List<string> rs = new List<string>();
            foreach (var s in strs)
            {
                string strValue = ReplaceSpecialChar(s);
                if (!string.IsNullOrEmpty(strValue))
                {
                    rs.Add(strValue);
                }
            }
            return rs;
        }

        private string ReplaceSpecialChar(string source)
        {
            return source.Replace("*", "").Replace("\r", "").Replace("\n", "").Replace(" ", "");
        }
        

    }
    /// <summary>
    /// 编译语言枚举
    /// </summary>
    public enum EnumCompilerLanguage
    {
        /// <summary>
        /// C#
        /// </summary>
        CSharp
    }
    /// <summary>
    /// 编译方式
    /// </summary>
    public enum EnumCompilerMode
    { 
        /// <summary>
        /// 程序集
        /// </summary>
        Assembly,
        /// <summary>
        /// 应用程序域
        /// </summary>
        AppDomian,
        /// <summary>
        /// Exe方式编译,执行Main入口函数
        /// </summary>
        Main,

    }

    /// <summary>
    /// 源码类型
    /// </summary>
    public enum EnumSourceType
    {
        /// <summary>
        /// 代码内容
        /// </summary>
        Code,
        /// <summary>
        /// 文件
        /// </summary>
        File,
      

    }
    /// <summary>
    /// 编译文件信息
    /// </summary>
    [Serializable]
    public class CompilerFileInfo
    {
        public string FileName = "";
        public string FullFileName = "";
        public DateTime LastWriteTime;

        public CompilerFileInfo()
        { }

        public CompilerFileInfo(string fileName):this(fileName,"")
        {
            
        } 
        
        public CompilerFileInfo(string fileName,string mainfilepath,bool ischeckfileexsit=true)
        {
            FileName = System.IO.Path.GetFileName(fileName);
            FullFileName = GetFullPath(mainfilepath, fileName, ischeckfileexsit);
            LastWriteTime = File.GetLastWriteTime(FullFileName);
        }



        private string GetFullPath(string fromFilePath, string relativeFilePath, bool ischeckfileexsit)
        {
            string path = IOHelper.GetFullPath(fromFilePath,relativeFilePath);
           
            if (!System.IO.File.Exists(path))
            {
                if (ischeckfileexsit == true)
                {
                    throw new NScriptException(string.Format("文件路径:{0}不存在", path));
                }
                else //文件路径不存在，则保留原始相对路径
                {
                    return relativeFilePath;
                }
            }
            return path;
        }
    }
}
